//
//  TabBar.swift
//  
//
//  Created by 孙长坦 on 2022/4/27.
//

import Foundation
import UIKit
import RxSwift

public class TabBar: UIView {
    public enum TabBarItemWidth {
        case automatic(min: CGFloat?, max: CGFloat?)
        
        case average
        
        case fixed(value: CGFloat)
    }
    
    private static let startTabBarItemButtonTag = 100000;
    
    private let disposeBag = DisposeBag()
    
    private let padding: UIEdgeInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
    
    public let contentView = UIScrollView()
    
    public var tabBarItems: [TabBarItem] {
        didSet{
            removeTabBarItems()
            addTabBarItems()
        }
    }
    
    public let tabBarItemWidth: TabBarItemWidth
    
    public var selectedIndex: Int? {
        didSet {
            guard oldValue != selectedIndex else {
                return
            }
            
            if let oldValue = oldValue, oldValue >= 0, oldValue < tabBarItemButtons.count {
                tabBarItemButtons[oldValue].setTitleColor(itemColor, for: .normal)
            }
            
            if let selectedIndex = selectedIndex, selectedIndex >= 0, selectedIndex < tabBarItemButtons.count {
                tabBarItemButtons[selectedIndex].setTitleColor(selectedItemColor, for: .normal)
                
                contentView.ex.scrollRectToVisibleCenteredOn(tabBarItemButtons[selectedIndex].frame, animated: true)
                
                tabView?.set(pagePosition: selectedIndex, animated: true)
            }
        }
    }
    
    public var itemColor: UIColor = Theme.shared.currentThemeData.topTabBarTextColor
    
    public var selectedItemColor: UIColor = Theme.shared.currentThemeData.topTabBarSelectedTextColor
    
    public var bottomBorderWidth: CGFloat = Theme.shared.currentThemeData.topTabBarBottomWidth
    
    public var bottomBorderColor: UIColor = Theme.shared.currentThemeData.topTabBarBottomBorderColor
    
    private var tabBarItemButtons: [UIButton] = []
    
    private let indicator: IndicatorProtocol
    
    public private(set) weak var tabView: TabView?
    
    public init(tabBarItems: [TabBarItem] = [],
                tabBarItemWidth: TabBarItemWidth = .average,
                selectedIndex: Int? = nil,
                indicator: IndicatorProtocol = LineIndicator(),
                frame: CGRect = .zero) {
        self.tabBarItems = tabBarItems
        self.tabBarItemWidth = tabBarItemWidth
        self.selectedIndex = selectedIndex
        self.indicator = indicator
        
        super.init(frame: frame)
        
        backgroundColor = Theme.shared.currentThemeData.topTabBarBackgroundColor
        
        ex.addBottomBorder(color: bottomBorderColor, width: bottomBorderWidth)
        
        contentView.showsHorizontalScrollIndicator = false
        contentView.showsVerticalScrollIndicator = false
        addSubview(contentView)
        
        addTabBarItems()
        
        contentView.layer.addSublayer(indicator)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func layoutSubviews() {
        super.layoutSubviews()
        
        contentView.frame = CGRect(x: padding.left, y: padding.top,
                                   width: bounds.width - padding.left - padding.right,
                                   height: bounds.height - bottomBorderWidth - padding.top - padding.bottom)
        
        var buttonStart: CGFloat = 0
        for button in tabBarItemButtons {
            let buttonWidth = getTabBarItemButtonWidth(tabBarItemWidth: tabBarItemWidth,
                                                       button: button)
            button.frame = CGRect(x: buttonStart, y: 0, width: buttonWidth, height: contentView.bounds.height)
            buttonStart += buttonWidth
        }
        
        contentView.contentSize = CGSize(width: buttonStart, height: contentView.bounds.size.height)
        
        if let selectedIndex = selectedIndex {
            setIndicator(selectedIndex: selectedIndex, animated: false)
            
            if selectedIndex < tabBarItemButtons.count {
                contentView.ex.scrollRectToVisibleCenteredOn(tabBarItemButtons[selectedIndex].frame, animated: false)
            }
        }
    }
    
    private func getTabBarItemButtonWidth(tabBarItemWidth: TabBarItemWidth, button: UIButton) -> CGFloat {
        switch tabBarItemWidth {
        case .automatic(let min, let max):
            let min = min ?? 0
            let max = max ?? contentView.bounds.width
            
            let size = button.sizeThatFits(CGSize(width: max, height: contentView.bounds.height))
            let width = size.width > min ? size.width : min
            return width + 24
            
        case .average:
            return tabBarItems.count == 0
            ? contentView.bounds.width
            : contentView.bounds.width / CGFloat(tabBarItems.count)
            
        case .fixed(let value):
            return value
        }
    }
    
    private func itemTextColors(startColor: UIColor, endColor: UIColor, percent: CGFloat) -> (UIColor, UIColor) {
        var startR: CGFloat = 0
        var startG: CGFloat = 0
        var startB: CGFloat = 0
        var startA: CGFloat = 0
        var endR: CGFloat = 0
        var endG: CGFloat = 0
        var endB: CGFloat = 0
        var endA: CGFloat = 0
        
        startColor.getRed(&startR, green: &startG, blue: &startB, alpha: &startA)
        endColor.getRed(&endR, green: &endG, blue: &endB, alpha: &endA)
        
        return (UIColor(red: startR + (endR - startR) * percent,
                        green: startG + (endG - startG) * percent,
                        blue: startB + (endB - startB) * percent,
                        alpha: startA + (endA - startA) * percent),
                UIColor(red: endR + (startR - endR) * percent,
                        green: endG + (startG - endG) * percent,
                        blue: endB + (startB - endB) * percent,
                        alpha: endA + (startA - endA) * percent))
    }
    
    public func attachTabView(tabView: TabView) {
        self.tabView = tabView
        tabView.set(pagePosition: selectedIndex ?? 0, animated: false)
        
        tabView.pagePositionObservable
            .subscribe { [weak self] pagePosition in
                self?.selectedIndex = pagePosition
            }
            .disposed(by: disposeBag)
        
        tabView.contentView.rx.contentOffset
            .subscribe { [weak self] value in
                guard let strongSelf = self, let point = value.element, tabView.bounds.width > 0 else {
                    return
                }
                
                var from: CGFloat
                var to: CGFloat
                var percent: CGFloat
                if tabView.contentView.isDragging || tabView.contentView.isDecelerating {
                    from = CGFloat(strongSelf.selectedIndex ?? 0)
                    to = point.x / tabView.bounds.width
                    to = from > to ? floor(to) : ceil(to)
                    percent = (point.x - tabView.bounds.width * from) / tabView.bounds.width
                    
                    if percent > 1 {
                        from += floor(percent)
                        percent = percent.truncatingRemainder(dividingBy: 1.0)
                    } else if percent < -1 {
                        from += ceil(percent)
                        percent = percent.truncatingRemainder(dividingBy: 1.0)
                    }
                } else {
                    from = CGFloat(tabView.pagePosition)
                    to = CGFloat(strongSelf.selectedIndex ?? 0)
                    from = from > to ? ceil(from) : floor(from)
                    percent = (point.x - tabView.bounds.width * from) / (tabView.bounds.width * abs(to - from))
                }
                
                guard to >= 0, to < CGFloat(strongSelf.tabBarItems.count), from != to else {
                    return
                }
                
                let fromItem = strongSelf.tabBarItemButtons[Int(from)]
                let toItem = strongSelf.tabBarItemButtons[Int(to)]
                
                let colors = strongSelf.itemTextColors(startColor: strongSelf.selectedItemColor,
                                                       endColor: strongSelf.itemColor,
                                                       percent: abs(percent))
                fromItem.setTitleColor(colors.0, for: .normal)
                toItem.setTitleColor(colors.1, for: .normal)
                
                let indicatorFrame = strongSelf.indicator.onMove(fromItem: fromItem, toItem: toItem, movePrecent: percent)
                
                strongSelf.contentView.ex.scrollRectToVisibleCenteredOn(indicatorFrame, animated: true)
                
            }
            .disposed(by: disposeBag)
    }
    
    func addTabBarItems() {
        tabBarItemButtons = tabBarItems.enumerated().map({ (index, tabBarItem) in
            let button = UIButton()
            button.tag = TabBar.startTabBarItemButtonTag + index
            button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 16)
            button.setTitle(tabBarItem.title, for: .normal)
            button.setTitleColor(selectedIndex == index ? selectedItemColor : itemColor, for: .normal)
            button.addTarget(self, action: #selector(onTabBarItemButtonClick(_:)), for: .touchUpInside)
            contentView.addSubview(button)
            return button
        })
        setNeedsLayout()
    }
    
    func removeTabBarItems() {
        tabBarItemButtons.forEach({ button in
            button.removeFromSuperview()
        })
        
        tabBarItemButtons.removeAll()
    }
    
    @objc func onTabBarItemButtonClick(_ button: UIButton) {
        selectedIndex = button.tag - TabBar.startTabBarItemButtonTag
    }
    
    func setIndicator(selectedIndex: Int?, animated: Bool) {
        guard let selectedIndex = selectedIndex, tabBarItemButtons.count > selectedIndex else {
            indicator.isHidden = true
            return
        }
        
        indicator.isHidden = false
        
        indicator.moveTo(item: tabBarItemButtons[selectedIndex], animated: animated)
    }
}
